package de.hauschild.gmltracer.tracer.impl;

import java.awt.Color;

import org.apache.commons.math.geometry.Vector3D;
import org.apache.commons.math.linear.RealMatrix;

import de.hauschild.gmltracer.tracer.Ray;

/**
 * Utilities for the renderer.
 * 
 * @author Klaus Hauschild
 * @since 1.0.0
 */
public class RendererUtils {

  /**
   * Builds the ray from the viewer (at (0, 0, -1)) into the scene.
   * 
   * @param x
   *          the column of the image
   * @param y
   *          the row of the image
   * @param fov
   *          the field of view
   * @param width
   *          the width of the generated image
   * @param height
   *          the height of the generated image
   * @return the ray
   */
  public static Ray buildRay(final int x, final int y, final double fov, final int width, final int height) {
    final double worldWidth = 2.0 * Math.tan(Math.toRadians(0.5 * fov));
    final double worldHeight = worldWidth * width / height;
    final double pixelSizeX = worldWidth / width;
    final double pixelSizeY = worldHeight / height;

    // return new Ray(new Vector3D(0.0, 0.0, -1.0), new Vector3D(-(worldWidth - 2 * (x + 0.5) * pixelSize), worldHeigt -
    // 2
    // * (y + 0.5) * pixelSize, 1.0));
    return new Ray(new Vector3D(0.0, 0.0, -1.0), new Vector3D(-(worldWidth - 2 * (x + 0.5) * pixelSizeX), worldHeight
        - 2 * (y + 0.5) * pixelSizeX, 1.0));
  }

  /**
   * Converts a point to color.
   * 
   * @param point
   *          the point
   * @return the color
   */
  public static Color pointToColor(final Vector3D point) {
    return new Color(normalizeTo255(point.getX()), normalizeTo255(point.getY()), normalizeTo255(point.getZ()));
  }

  /**
   * Transforms the vector represented by the point with the matrix.
   * 
   * @param matrix
   *          the matrix
   * @param point
   *          the point
   * @return the point
   */
  public static Vector3D transform(final RealMatrix matrix, final Vector3D point) {
    final double[] operated = matrix.operate(new double[] { point.getX(), point.getY(), point.getZ(), 1.0 });
    return new Vector3D(operated[0], operated[1], operated[2]);
  }

  private static int normalizeTo255(final double value) {
    return (int) (255 * Math.min(1.0, value));
  }
}
